import * as THREE from "three"
import playerConst from "../constant/player.const"
import CameraData from "../data/CameraData"
import FloorsViewData from "../data/FloorsViewData"
import SettingsData from "../data/SettingsData"
import SweepData from "../data/SweepData"
import TourData from "../data/TourData"
import ViewmodeData from "../data/ViewmodeData"
import EngineContext from "../EngineContext"
import optionsKeyEnum from "../enum/optionsKey.enum"
import { TourTransitionList, TourTransitionType } from "../enum/tourTransition.enum"
import transitionTypeEnum from "../enum/transitionType.enum"
import viewmodeEnum from "../enum/viewmode.enum"
import y from "../math/19674"
import b from "../math/20601"
import T from "../math/7170"
import CameraModule from "../module/CameraModule"
import CommonControlsModule from "../module/CommonControlsModule"
import SweepDataModule from "../module/SweepDataModule"
import SweepPathModule from "../module/SweepPathModule"
import ViewmodeModule from "../module/ViewmodeModule"
import Snapshot from "../object/Snapshot"
import n from "../packages/77037"
import Vectors from "../three/Vectors"
import funcUtil from "../utils/func.util"
import TourDefaultTransition from "./TourDefaultTransition"
import TourDelayTransition from "./TourDelayTransition"
import TourDirectWalkingTransition from "./TourDirectWalkingTransition"
import TourDollhousePanTransition from "./TourDollhousePanTransition"
import TourFloorChangeTransition from "./TourFloorChangeTransition"
import TourInstantTransition from "./TourInstantTransition"
import TourPanTransition from "./TourPanTransition"
import TourPanTransitionLegacy from "./TourPanTransitionLegacy"
import TourSmoothWalkingTransition from "./TourSmoothWalkingTransition"
import TourZoomTransition from "./TourZoomTransition"
const _ = {
  sharpTurnDotThreshold: 0.65,
  directionWeightFactorStd: 0.75,
  directionWeightFactorSharp: 0.2,
  positionalWeightFactorStd: 0.4,
  positionalWeightFactorSharp: 0.2,
  finalWalkingNodeDirectionWeight: 5,
  lookAheadNodes: 3
}
class OrientationHelper {
  settings: {
    sharpTurnDotThreshold: number
    directionWeightFactorStd: number
    directionWeightFactorSharp: number
    positionalWeightFactorStd: number
    positionalWeightFactorSharp: number
    finalWalkingNodeDirectionWeight: number
    lookAheadNodes: number
  }
  constructor(e = _) {
    this.settings = e
  }
  getOrientationsForPath(e: THREE.Vector3[], t: THREE.Quaternion) {
    const i = []
    for (let s = 0; s < e.length; s++) {
      const n = new THREE.Vector3()
      this.getLookVectorsForPathNode(e, s, t, n)
      const r = new THREE.Matrix4().lookAt(e[s], n, Vectors.UP)
      i[s] = new THREE.Quaternion().setFromRotationMatrix(r)
    }
    return i.push(t), i
  }
  getLookVectorsForPathNode(e, t, i, s) {
    const n = new THREE.Vector3()
    const r = new THREE.Vector3()
    const o = new THREE.Vector3()
    const a = new THREE.Vector3()
    const h = e.length
    if (t >= h) return !1
    let l = 1,
      d = 1
    const c = new THREE.Vector3()
    let u
    for (let s = t; s < t + this.settings.lookAheadNodes && s < h; s++) {
      if (((u = e[s]), this.getOrientationForPathNode(e, s, i, o), s === t && n.copy(o), s > t)) {
        const e = n.dot(o) < this.settings.sharpTurnDotThreshold
        ;(l *= e ? this.settings.directionWeightFactorSharp : this.settings.directionWeightFactorStd),
          (d *= e ? this.settings.positionalWeightFactorSharp : this.settings.positionalWeightFactorStd)
      }
      s === h - 1 && ((l = this.settings.finalWalkingNodeDirectionWeight), (d = 1)), c.copy(o), o.multiplyScalar(l), r.add(o), a.lerp(u, d)
    }
    r.normalize()
    s.copy(a)
    s.add(r)
    return !0
  }
  getOrientationForPathNode(e, t, i, s) {
    if (t >= e.length) return !1
    if (t === e.length - 1) s.copy(Vectors.FORWARD).applyQuaternion(i)
    else {
      const i = e[t],
        n = e[t + 1]
      s.copy(n).sub(i)
    }
    s.normalize()
    return !0
  }
}
export default class TourTransitionFactory {
  static getOtherModeTransitionTime: (...e) => number
  static getTransitionTime: (...e) => number
  static getSamePanoTransitionTime: (...e) => number
  static getTransitionSpeed: (...e) => number
  engine: EngineContext
  tourData: TourData
  cameraData: CameraData
  settingsData: SettingsData
  viewmodeData: ViewmodeData
  sweepModule: SweepDataModule
  sweepData: SweepData
  cameraModule: CameraModule
  pathModule: SweepPathModule
  floorsViewData: FloorsViewData
  commonControlsModule: CommonControlsModule
  viewmodeModule: ViewmodeModule
  smoothWalkingTransition: TourSmoothWalkingTransition
  directWalkingTransition: TourDirectWalkingTransition
  instantTransition: TourInstantTransition
  panTransition: TourPanTransition
  panLegacyTransition: TourPanTransitionLegacy
  panDollhouseTransition: TourDollhousePanTransition
  defaultTransition: TourDefaultTransition
  zoomTransition: TourZoomTransition
  floorChangeTransition: TourFloorChangeTransition
  delayTransition: TourDelayTransition
  nopTransition: { active: boolean; type: number; promise: Promise<void>; stop: () => Promise<void>; toIndex: number }
  pathOrientHelper: OrientationHelper
  getValidWalkingPath: (e: Snapshot) => any
  static useDelayTransition: any
  static getPanDirection: (e: SettingsData, t?: number) => number
  static getDelayDuration: (e?) => number
  static getZoomDuration: (e: SettingsData) => number
  static getPanValues: (...e) => { degrees: number; radiansPerMs: number; ms: number; direction: number }
  constructor(e, t, i, s, r) {
    this.engine = e
    this.tourData = t
    this.cameraData = i
    this.settingsData = s
    this.viewmodeData = r
    this.getValidWalkingPath = e => {
      const t = e.metadata.scanId
      const i = this.sweepData.currentSweep
      const s = this.tourData.getTourCurrentSnapshotIndex()
      let r = null
      if (s >= 0) {
        const e = this.tourData.getTourSnapshotSid(s)
        r = this.tourData.getSnapshot(e)
      }
      if (this.viewmodeData.currentMode !== viewmodeEnum.Panorama || !i || !t || t === i || e.is360 || !r || r.is360) return null
      const a =
        this.pathModule.findShortestPath(
          i,
          t,
          n.Xd.walkingTourIncludeExtraPanosDistance,
          n.Xd.walkingStageMinimumDistance,
          n.Xd.maxWalkingSweepsBetweenSnapshots
        ) || []
      return 0 === a.length ? null : a
    }
  }
  async init() {
    this.sweepModule = await this.engine.getModule(SweepDataModule)
    this.sweepData = await this.engine.market.waitForData(SweepData)
    this.cameraModule = await this.engine.getModule(CameraModule)
    this.pathModule = await this.engine.getModule(SweepPathModule)
    this.floorsViewData = await this.engine.market.waitForData(FloorsViewData)
    this.commonControlsModule = await this.engine.getModule(CommonControlsModule)
    this.viewmodeModule = await this.engine.getModule(ViewmodeModule)
    this.smoothWalkingTransition = new TourSmoothWalkingTransition(
      this.settingsData,
      this.cameraData.pose,
      this.cameraData.transition,
      this.sweepData.transition,
      this.sweepModule,
      this.cameraModule,
      this.engine,
      this.pathModule.setRestrictedSweeps.bind(this.pathModule),
      this.pathModule.getCurveForPath.bind(this.pathModule)
    )
    this.directWalkingTransition = new TourDirectWalkingTransition(
      this.settingsData,
      this.cameraData.pose,
      this.sweepModule.moveToSweep.bind(this.sweepModule),
      this.cameraModule.updateTransitionSpeed.bind(this.cameraModule),
      this.pathModule.setRestrictedSweeps.bind(this.pathModule),
      this.engine
    )
    this.instantTransition = new TourInstantTransition(
      this.sweepModule.moveToSweep.bind(this.sweepModule),
      this.viewmodeData,
      this.cameraModule,
      this.viewmodeModule.switchToMode.bind(this.viewmodeModule)
    )
    this.panTransition = new TourPanTransition(
      this.settingsData,
      this.commonControlsModule.startRotateTransition.bind(this.commonControlsModule),
      this.commonControlsModule.stop.bind(this.commonControlsModule)
    )
    this.panLegacyTransition = new TourPanTransitionLegacy(
      this.settingsData,
      this.cameraData.pose,
      this.commonControlsModule.startRotateTransition.bind(this.commonControlsModule),
      this.commonControlsModule.stop.bind(this.commonControlsModule),
      this.pathModule.getCurveForPath.bind(this.pathModule)
    )
    this.panDollhouseTransition = new TourDollhousePanTransition(
      this.settingsData,
      this.commonControlsModule.startRotateTransition.bind(this.commonControlsModule),
      this.commonControlsModule.stop.bind(this.commonControlsModule)
    )
    this.defaultTransition = new TourDefaultTransition(
      this.settingsData,
      this.cameraData.pose,
      this.viewmodeData,
      this.cameraModule,
      this.sweepModule,
      this.viewmodeModule.switchToMode.bind(this.viewmodeModule),
      this.pathModule.setRestrictedSweeps.bind(this.pathModule),
      this.engine
    )
    this.zoomTransition = new TourZoomTransition(
      this.settingsData,
      this.commonControlsModule.startZoomTransition.bind(this.commonControlsModule),
      this.commonControlsModule.stop.bind(this.commonControlsModule)
    )
    this.floorChangeTransition = new TourFloorChangeTransition(
      this.engine.commandBinder.issueCommand.bind(this.engine.commandBinder),
      () => this.floorsViewData.currentFloorId
    )
    this.delayTransition = new TourDelayTransition(this.settingsData)
    this.nopTransition = {
      active: !1,
      type: TourTransitionType.Nop,
      promise: Promise.resolve(),
      stop: () => Promise.resolve(),
      toIndex: -1
    }
    this.pathOrientHelper = new OrientationHelper()
  }
  static getPanDegrees(e, t) {
    let i
    i = n.Xd.panAtSnapshot
      ? -1 !== funcUtil.getUrlParams("sspa", -1)
        ? n.Xd.snapshotPanAngle
        : void 0 !== t
        ? t
        : e.tryGetProperty(optionsKeyEnum.PanAngle, n.BA)
      : 0
    return Math.max(i, 0)
  }
  static getPanRadiansPerMs(e, t, i) {
    if (-1 !== funcUtil.getUrlParams("st", -1)) return n.Xd.snapshotPanZoomDuration > 0 ? i / n.Xd.snapshotPanZoomDuration : 0
    const s = t ? e.tryGetProperty(optionsKeyEnum.DollhousePanSpeed, n.su) : e.tryGetProperty(optionsKeyEnum.PanSpeed, n.pn)
    return b.DA(s)
  }
  getFloorTransition(e: number) {
    const t = this.tourData.getTourSnapshotSid(e)
    const targetSnapshot = this.tourData.getSnapshot(t)
    if (!targetSnapshot) return this.nopTransition
    return this.floorChangeTransition.start({ targetSnapshot })
  }
  getMainTransition(e: number, transitionType: number) {
    const sid = this.tourData.getTourSnapshotSid(e)
    const snapshot = this.tourData.getSnapshot(sid)
    if (!snapshot) return this.nopTransition
    let TransitionClass = null
    if (transitionType === transitionTypeEnum.Interpolate) {
      const cameraQuaternion = snapshot.metadata.cameraQuaternion
      const path = this.getValidWalkingPath(snapshot)
      if (path) {
        const position: THREE.Vector3[] = path.map(e => e.position)
        const orientations = this.pathOrientHelper.getOrientationsForPath(position, y.Z(cameraQuaternion))
        TransitionClass = this[path.length > 2 ? "smoothWalkingTransition" : "directWalkingTransition"].start({
          path,
          orientations
        })
      }
    } else if (transitionType === transitionTypeEnum.Instant) {
      const currentSweep = this.sweepData.currentSweep
      TransitionClass = this.instantTransition.start({ snapshot, currentSweep })
    }
    if (!TransitionClass) {
      const currentSweep = this.sweepData.currentSweep
      TransitionClass = this.defaultTransition.start({
        snapshot,
        currentSweep,
        transitionType
      })
    }
    return TransitionClass
  }
  getBurnsTransition(e: number, t: number) {
    const i = this.tourData.getTourSnapshotSid(e)
    const s = (e + 1) % this.tourData.getSnapshotCount()
    const n = this.tourData.getTourSnapshotSid(s)
    const r = this.tourData.getHighlightDescription(i)
    const o: any = {}
    r.reelEntry && r.reelEntry.overrides && ((o.panDirection = r.reelEntry.overrides.panDirection), (o.panAngle = r.reelEntry.overrides.panAngle))
    const a = this.tourData.getSnapshot(n)
    let h = this.nopTransition
    switch (t) {
      case TourTransitionList.Pan:
        if (a) {
          const e = this.getValidWalkingPath(a)
          h = this.panLegacyTransition.start({
            path: e,
            snapshot: r.snapshot,
            nextSnapshot: a,
            panOverrides: o
          })
        }
        h === this.nopTransition &&
          (h = this.panTransition.start({
            snapshot: r.snapshot,
            nextSnapshot: a,
            panOverrides: o
          }))
        break
      case TourTransitionList.PanDollhouse:
        h = this.panDollhouseTransition.start({
          snapshot: r.snapshot,
          nextSnapshot: a,
          panOverrides: o
        })
        break
      case TourTransitionList.Zoom:
        h = this.zoomTransition.start()
        break
      case TourTransitionList.Delay:
        h = this.delayTransition.start()
        break
      case TourTransitionList.None:
        h = this.nopTransition
        break
      default:
        throw Error("unhandled TourBurnsStyle")
    }
    return h
  }
}
TourTransitionFactory.useDelayTransition = (e, t, i) =>
  !n.Xd.panAtSnapshot || (t !== TourTransitionList.Zoom && 0 === TourTransitionFactory.getPanDegrees(e, i))
TourTransitionFactory.getPanDirection = (e, t): number => (void 0 !== t ? t : e.tryGetProperty(optionsKeyEnum.PanDirection, playerConst.pan_direction))
TourTransitionFactory.getDelayDuration = e =>
  -1 !== funcUtil.getUrlParams("st", -1) || 0 === funcUtil.getUrlParams("kb", -1) ? n.Xd.snapshotPanZoomDuration : n.mS
TourTransitionFactory.getZoomDuration = e =>
  -1 !== funcUtil.getUrlParams("st", -1) ? n.Xd.snapshotPanZoomDuration : e.tryGetProperty(optionsKeyEnum.ZoomDuration, n._c)
TourTransitionFactory.getPanValues = (e, t, i, s) => {
  const direction = TourTransitionFactory.getPanDirection(e, i)
  const degrees = TourTransitionFactory.getPanDegrees(e, s)
  const a = T.Id(degrees)
  const radiansPerMs = TourTransitionFactory.getPanRadiansPerMs(e, t, a)
  let ms = radiansPerMs > 0 ? a / radiansPerMs : 0
  t && void 0 === s && (ms = n.Xd.snapshotPanZoomDuration)
  return { degrees, radiansPerMs, ms, direction }
}
TourTransitionFactory.getTransitionSpeed = e => {
  if (-1 !== funcUtil.getUrlParams("wts", -1)) return e.tryGetProperty(n.EU, n.Im)
  const t = e.tryGetProperty(optionsKeyEnum.TransitionSpeed, n.Mk)
  return b.mf(t / 1000)
}
TourTransitionFactory.getTransitionTime = e => {
  const t = e.tryGetProperty(optionsKeyEnum.TransitionTime, n.mL)
  return Math.min(Math.max(n.eu, t), n.NY)
}
TourTransitionFactory.getOtherModeTransitionTime = (e, t, i) => {
  if (i === transitionTypeEnum.FadeToBlack) {
    return TourTransitionFactory.getTransitionTime(e) + n.HJ
  }
  let s = n.Cp
  let r = n.f7
  if (-1 === funcUtil.getUrlParams("wts", -1)) {
    const t = 1000 * TourTransitionFactory.getTransitionSpeed(e)
    ;(s = Math.sqrt(2 * (t - n.Pv)) + 45), t !== n.Mk && (r = n._D)
  }
  const o = (1000 * t) / (T.Ue * s)
  return Math.max(o, r)
}
TourTransitionFactory.getSamePanoTransitionTime = (e, t) => {
  const i = e.tryGetProperty(optionsKeyEnum.PanSpeed, n.pn)
  if (i === n.pn) return Math.max((1000 * t) / (T.Ue * n.O2), n.gS)
  {
    const e = y.dS(i, n.z$, n.b_, n.rM, n.z8),
      s = b.DA(e)
    return s > 0 ? t / s : 0
  }
}
